/*
 * Copyright (c) 2010 Mathew Hall, University of Sheffield.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * Neither the name of the University of Sheffield nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package search.genes;

import gpinterpreter.stack.StackInstruction;
import gpinterpreter.stack.StackSymbol;
import static gpinterpreter.stack.StackSymbol.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jgap.*;

public class StackInstructionGene extends BaseGene implements Gene {
	public int graphSize;

	private static StackSymbol[] symbols = { NOP,CLUSTER, PUSH};

	// TODO: profile this with and without SPLIT - why is split so cheap?
	public void setGraphSize(int s) {
		graphSize = s;
	}
        

	public int getGraphSize() {
		return graphSize;
	}
        
	@Override
	public StackInstructionGene clone(){
            StackInstructionGene sg;
            try {
                sg = new StackInstructionGene(this.getConfiguration(), inst.clone());
				sg.setGraphSize(graphSize);
            } catch (InvalidConfigurationException ex) {
                Logger.getLogger(StackInstructionGene.class.getName()).log(Level.SEVERE, null, ex);
                throw new RuntimeException("Attempt to clone SymbolInstructionGene failed");
            }
            return sg;
        }

	private static final long serialVersionUID = 1L;
	private StackInstruction inst;

	public StackInstructionGene() throws InvalidConfigurationException {
		this(null);

	}

	public StackInstructionGene(Configuration aConfiguration)
			throws InvalidConfigurationException {
		super(aConfiguration);
		inst = new StackInstruction(NOP);

	}

	public StackInstructionGene(Configuration aConfiguration, int gs)
			throws InvalidConfigurationException {
		super(aConfiguration);
		inst = new StackInstruction(NOP);
		graphSize = gs;

	}

	public StackInstructionGene(Configuration aConfiguration, StackInstruction i)
			throws InvalidConfigurationException {
		super(aConfiguration);
		inst = i.clone();
	}

	public StackInstructionGene(Configuration aConfiguration, StackInstruction i,
			int gs) throws InvalidConfigurationException {
		super(aConfiguration);
		inst = i.clone();
		graphSize = gs;
	}

	@Override
	protected Object getInternalValue() {
		if (inst == null)
			return inst;
		return inst.clone();
	}

	@Override
	protected Gene newGeneInternal() {
		try {
			return new StackInstructionGene(getConfiguration(), inst,
					graphSize);
		} catch (InvalidConfigurationException e) {
			throw new IllegalStateException(e.getMessage());

		}
	}

	@Override
	public void applyMutation(int index, double percentage) {

		StackInstruction toMutate = inst;
		double pc = percentage;
		percentage = Math.abs(percentage);

		if (pc >= 0) {
			
			double operand = toMutate.getOperand() + graphSize * pc;
			
			operand = operand % graphSize + 1;
			
			toMutate.setOperand((int) Math.round(operand));
			
		

		if (toMutate.getOperand() > graphSize)
			toMutate.setOperand((int) (toMutate.getOperand() % (double) graphSize));

			
			
		}else{ 


			double places = (symbols.length - 1) * percentage;
			switch (toMutate.getType()) {
			case PUSH:
				places++;
			case CLUSTER:
				places++;
			case NOP:
				places++;
			}
			places = Math.round(places) % (symbols.length);

			toMutate.setType(symbols[(int) places]);
		}




	}

	@Override
	public String getPersistentRepresentation()
			throws UnsupportedOperationException {

		return inst.toString();
	}

	@Override
	public void setAllele(Object arg0) {
		inst = ((StackInstruction) arg0).clone();
	}

	@Override
	public void setToRandomValue(RandomGenerator arg0) {


		double places = (symbols.length - 1) * arg0.nextDouble();
		places = Math.abs(Math.round(places) % (symbols.length));
                if(graphSize < 1){
                    System.err.println("graphSize is less than 1. Please check your input!");
                }
		inst = new StackInstruction(symbols[(int) places], arg0.nextInt(graphSize) + 1);

		StackInstruction lastInst = inst;
		if (lastInst.getOperand() <= 0)
			lastInst.setOperand(1);


	}

	@Override
	public void setValueFromPersistentRepresentation(String arg0)
			throws UnsupportedOperationException,
			UnsupportedRepresentationException {

			throw new UnsupportedOperationException(
					"Parse error on persistent representation");

	}

	@Override
	public int compareTo(Object o) {
		if (o == null)
			return 1;

		if (inst == null) {
			if (((StackInstructionGene) o).getAllele() == null) {
				return 0;
			} else {
				return -1;
			}
		}

		StackInstruction p2 = (StackInstruction) ((StackInstructionGene) o).getAllele();

		if (p2 != inst) {

			if (p2.getType() == NOP && inst.getType() != NOP)
				return 1;
			if (inst.getType() == NOP && p2.getType() != NOP)
				return -1;

		}

		return 0;
	}

	@Override
	public int hashCode() {
		return inst.hashCode() + new Integer(inst.getOperand()).hashCode();
	}
	
	
	public Object getAllele(){
		return getInstruction();
	}


	public StackInstruction getInstruction(){
		return inst;
	}
}
